package com.cande.loki.exercise.dynamicDatasource;

/**
 * @auther ROCK CODER
 * @DATE 2023/2/28 21:33
 * @Description
 */

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 切换数据源的切面
 */
@Component
@Aspect
@Order(1) //这是关键，要让该切面调用先于AbstractRoutingDataSource的determineCurrentLookupKey()AbstractRoutingDataSource的determineCurrentLookupKey()
public class DynamicDataSourceAspect {

    /**
     * within 对象级别，用在类上
     * annotation 方法级别，用在方法上
     */
    @Pointcut("@annotation(com.cande.loki.exercise.dynamicDatasource.DS)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 切换数据源
        try {
            // 获取类上定义的DataSource注解
            DS annotationOfClass = point.getTarget().getClass().getAnnotation(DS.class);
            // 获取方法名
            String methodName = point.getSignature().getName();
            // 拿到方法对应的参数类型
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            // 根据类、方法、参数类型（重载）获取到方法的具体信息
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            // 拿到方法定义的DataSource注解信息
            DS methodAnnotation = method.getAnnotation(DS.class);
            // 方法上的注解优先于类上的注解
            methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;

            // 获取DataSource注解指定的数据源
            ContextConst.DataSourceType dataSourceType = methodAnnotation != null ? methodAnnotation.value() : ContextConst.DataSourceType.PRIMARY;

            // 设置数据源
            DataSourceContextHolder.setDataSource(dataSourceType.name());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        // 执行方法
        try {
            return point.proceed();
        } finally {
            DataSourceContextHolder.clearDataSource();
        }
    }
}
